#include <iostream>

/////////////////////////////// EBO //////////////////////////////////
class A
{
};

class HoldsAnIntAndAnA
{
  int n;
  A a;
};
#include <tr1/memory>

class HoldsAnInt : A
{
  int n;
};

/////////////////////// Strategy Pattern //////////////////////////////

class HealthCalculator
{
public:
  virtual int calc(int) = 0;
};

class EvilBadGuy: public HealthCalculator
{
public:
  int calc(int h) { return h*h; }
};

class GameCharacter
{
public:
  GameCharacter() : health(100) {}
  int getHealth() { return calculator->calc(health); }
  std::tr1::shared_ptr<HealthCalculator> calculator;
  int health;
};

///// Strategy Pattern with function pointers +explicit ////////////////

int calcEvil(int h)
{
  return h*h;
}

int calcGood(int h)
{
  return h;
}

class GameCharacterF
{
public:
  typedef int (*HealthCalculator)(int);
  
  explicit GameCharacterF(HealthCalculator hcf) 
  : calculator(hcf), health(100) {
    std::cout << "(Constructor)" << std::endl;
  }
  int getHealth() { return calculator(health); }
  
  
private:
  HealthCalculator calculator;
  
  int health;
};

///////////////////////// static cast  //////////////////////////////////
class AA
{
public:
  virtual void f() {std::cout << "AA:f()" << std::endl;}
};

class BB: public AA
{
public:
  void f() {std::cout << "BB:f()" << std::endl;}
};

class CC: public AA
{
public:
  CC() : n(999) {}
  void f() {std::cout << "CC:f()" << std::endl;}
  void g() {std::cout << "CC:g() n=" << n << std::endl;}
private:
  int n;
};

///////////////////////// operator*  //////////////////////////////////
class Rational
{
public:
  Rational(int d = 0, int n = 1)
  :d_(d), n_(n) {}
  
  //Rational operator*(const Rational& rhs);
  friend Rational operator*(const Rational& a, const Rational &b);
  
  void print() const { std::cout << d_ << "/" << n_ << std::endl;}
  
private:
  int d_;
  int n_;
};

/*Rational Rational::operator*(const Rational& rhs)
{
    Rational r;
    r.d_ = d_ * rhs.d_;
    r.n_ = n_ * rhs.n_;
    return r;
  }
*/
Rational operator*(const Rational& a, const Rational &b)
{
    Rational r;
    r.d_ = a.d_ * b.d_;
    r.n_ = a.n_ * b.n_;
    return r;
  }

////////// static_cast: a copy will be modified ///////////////////////
class BBB
{
public:
  BBB(): n(37) {}
  void change() {
    n++;
  }
protected:
  int n;
};

class AAA: public BBB
{
public:	
  void change(int dummy) {}
  void changeBaseCast() {
    static_cast<BBB>(*this).change();
  }
  void changeBaseScope() {
    BBB::change();
  }
  void printBase() const {
    std::cout << n << std::endl;
  }
};

////////////////////// using ///////////////////////////
class BBBB
{
public:
  void f(int) {}
  void f(double) {}
};

class AAAA: public BBBB
{
public:
  using BBBB::f;
  void f(std::string) {}
};

///////////////// swap, Koenig Lookup ////////////////
namespace WidgetStuff{
  
class Widget
{
public:
  Widget(int n);
  void print();
  Widget& swap(Widget& rhs) {
    Widget w(rhs);
    rhs = *this;
    *this = w;
    
    return *this;
  }
private:
  int n_;
};

WidgetStuff::Widget::Widget(int n) : n_(n) {}
void Widget::print() {
  std::cout << "Widget n=" << n_ << std::endl;
}

void swap(WidgetStuff::Widget w1, WidgetStuff::Widget w2)
{
  std::cout << "WidgetStuff::swap" << std::endl;
  w1.swap(w2);
}

} // namespace WidgetStuff

namespace std {
  template<>
  void swap<WidgetStuff::Widget>(WidgetStuff::Widget& a, WidgetStuff::Widget& b) {
    std::cout << "std::swap" << std::endl;
    a.swap(b);
  }
}


//////////////// multiple inheritance /////////////////

class Common
{
public:
  int d;
};

class One: virtual public Common
{
public:
  One() : n(17) {}
  int n;
};

class Two: virtual Common
{
public:
  Two() : n(19) {}
  int n;
};

class Sub: public One, public Two
{
public:
  void print() { 
    std::cout << "Sub::print ->" << std::endl;
    std::cout << One::n << std::endl;
    std::cout << Two::n << std::endl; 
    std::cout << One::d << std::endl;
    std::cout << Two::d << std::endl;
    std::cout << "Sub::print <-" << std::endl;
  }
};

////////////////////////// main /////////////////////////
int main(int argc, char*argv[])
{
  // EBO
  std::cout << sizeof(A) << std::endl;
  std::cout << sizeof(HoldsAnIntAndAnA) << std::endl;
  std::cout << sizeof(HoldsAnInt) << std::endl;
  
  // Strategy Pattern
  std::cout << "Strategy Pattern" << std::endl;
  std::tr1::shared_ptr<HealthCalculator> evilGuy(new EvilBadGuy());
  
  GameCharacter gc;
  
  std::cout << gc.calculator.get() << std::endl;
  std::cout << evilGuy.get() << std::endl;
    
  gc.calculator = evilGuy;
  
  std::cout << gc.calculator.get() << std::endl;
  std::cout << evilGuy.get() << std::endl;
  
  std::cout << gc.getHealth() << std::endl;
  
  // Strategy Pattern with function pointers + explicit
  std::cout << "Strategy Pattern with function pointers" << std::endl;
  GameCharacterF gcf(&calcEvil);
  std::cout << gcf.getHealth() << std::endl;
  GameCharacterF gcf2(&calcGood);
  //gcf2 = calcEvil; //this is forbidden by the explicit constructor
  std::cout << gcf2.getHealth() << std::endl;
  
  // static cast
  BB bb;
  AA *aa = &bb;
  aa->f();
  static_cast<BB*>(aa)->f();  
  CC *cc = static_cast<CC*>(aa);
  cc->f();
  cc->g();
  
  //BB *bbgc = (BB*) &gc; // causes a segmentation fault
  //bbgc->f();
  
  // operator*
  Rational r1(1, 2);
  Rational r2(1);
  r2 = 100;
  Rational r3 ;
  
  r1.print();
  r2.print();
  r3.print();
  
  std::cout << "r4:\n";
  Rational r4;
  r4 = r1*r2;
  r4.print();
  r4 = r1*2;
  r4.print();
  r4 = 4*r1;
  r4.print();
  
  // Strategy Pattern with function pointers + explicit
  std::cout << "////////// static_cast: a copy will be modified ///////////////////////" << std::endl;
  AAA a;
  a.printBase();
  a.changeBaseCast();
  a.printBase();
  a.changeBaseScope();
  a.printBase();
  
  // /////////////////// using
  
  std::cout << "/////////////// using /////////////" << std::endl;
  AAAA aaaa;
  aaaa.f(std::string("bla"));
  aaaa.f(2); //only works because of "using"
  std::cout << "(it worked, if no error messages)" << std::endl;
  
  ///////////////// swap, Koenig Lookup ////////////////
  std::cout << "/////////////// swap, Koenig Lookup /////////////" << std::endl;
  
  using std::swap;
  
  WidgetStuff::Widget w1(2);
  WidgetStuff::Widget w2(0); w2=200;
  w1.print();
  w2.print();
  swap(w1,w2);
  w1.print();
  w2.print();
  
  ////////////////// multiple inheritance
  Sub s;
  s.print();
  
  
  
  
  return 0;
}